package secondProject;

import framework.Action;
import framework.Percept;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.jgrapht.alg.DijkstraShortestPath;
import vacuumAgent.VAAction;
import vacuumAgent.VAAgent;
import vacuumAgent.VAFloor;
import vacuumAgent.VAPercept;
import vacuumAgent.VATile.VATileStatus;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleDirectedWeightedGraph;
import org.jgrapht.graph.SimpleWeightedGraph;

import vacuumAgent.VAConvertToGraph;

public class SecondAgent extends VAAgent {

    Point originalPosition = null;
    Point currentPosition = null;
    Point currentDest = null;
    int[][] Mapa = null;
    List<Point> path = new ArrayList<Point>();
    List<Caso> nonVisitato = null;
    SimpleWeightedGraph<Point, DefaultWeightedEdge> Grafo = new SimpleWeightedGraph<Point, DefaultWeightedEdge>(DefaultWeightedEdge.class);
    boolean reached = true;
    boolean test = false;

    public SecondAgent(int energy) {
        super(energy);

        nonVisitato = new ArrayList<Caso>();
    }

    private void setmapa(int size, VAPercept percept) {
        VAPercept p = (VAPercept) percept;
        Point po = p.getVacuumAgentPosition();
        Grafo.addVertex(new Point(po.x, po.y));
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                Mapa[i][j] = -1;

            }
        }

    }

    private boolean addVertex(Point p) {
        boolean added = false;
        if (!Grafo.containsVertex(p)) {
            Grafo.addVertex(p);

            int x = p.x;
            int y = p.y;

            Point p1 = new Point(x, y - 1);
            Point p2 = new Point(x + 1, y);
            Point p3 = new Point(x, y + 1);
            Point p4 = new Point(x - 1, y);
            if (Grafo.containsVertex(p1)) {
                Grafo.addEdge(p, p1);
                Grafo.addEdge(p1, p);

            }
            if (Grafo.containsVertex(p2)) {
                Grafo.addEdge(p, p2);
                Grafo.addEdge(p2, p);
            }
            if (Grafo.containsVertex(p3)) {
                Grafo.addEdge(p, p3);
                Grafo.addEdge(p3, p);
            }
            if (Grafo.containsVertex(p4)) {
                Grafo.addEdge(p, p4);
                Grafo.addEdge(p4, p);
            }
            added = true;
        }
        return added;
    }

    private void setPolvere() {
        nonVisitato.clear();

        for (int i = 0; i < Mapa.length; i++) {
            for (int j = 0; j < Mapa.length; j++) {
                Point tmp = new Point(i, j);

                if (Mapa[i][j] == 5) {
                    Grafo.addVertex(originalPosition);
                    List chemin = DijkstraShortestPath.findPathBetween(Grafo, currentPosition, tmp);
                    int distance = chemin.size();
                    nonVisitato.add(new Caso(tmp, distance));
                }
            }
        }
    }

    private void UpDate(VAPercept percept) {
        VAPercept p = (VAPercept) percept;

        int size = p.getFloor().getSize();

        Point posizioneattuale = p.getVacuumAgentPosition();
        Mapa[posizioneattuale.x][posizioneattuale.y] = 0;
        Point nordo = new Point(posizioneattuale.x - 1, posizioneattuale.y);
        Point sinistra = new Point(posizioneattuale.x, posizioneattuale.y - 1);
        Point destra = new Point(posizioneattuale.x, posizioneattuale.y + 1);
        Point sotto = new Point(posizioneattuale.x + 1, posizioneattuale.y);
        addVertex(posizioneattuale);


        // System.out.println("Mapasss["+sotto.x+"]["+sotto.y+"]="+Mapa[sotto.x][sotto.y]);}

        if (!p.getNeighborhood().northIsFree() && (posizioneattuale.x != size - 1)) {
            Mapa[sotto.x][sotto.y] = 1;
            System.out.println("&&&&&&&&&&&&&&&&nordnotfree");
        }

        if (!p.getNeighborhood().southIsFree() && (posizioneattuale.x != 0)) {
            Mapa[nordo.x][nordo.y] = 1;
            System.out.println("&&&&&&&&&&&&&&&&&&&southnotfree");
        }
        //System.out.println("Mapannn["+nordo.x+"]["+nordo.y+"]="+Mapa[nordo.x][nordo.y]);}

        if ((!p.getNeighborhood().eastIsFree()) && (posizioneattuale.y != size - 1)) {
            Mapa[destra.x][destra.y] = 1;
        }

        if (!p.getNeighborhood().westIsFree() && (posizioneattuale.y != 0)) {
            Mapa[sinistra.x][sinistra.y] = 1;
        }

        if ((p.getNeighborhood().southIsFree()) && (posizioneattuale.x != 0) && (Mapa[nordo.x][nordo.y] == -1)) { //currentDest = nordo;
            Mapa[nordo.x][nordo.y] = 5;
            addVertex(nordo);
        }

        if ((p.getNeighborhood().eastIsFree()) && (posizioneattuale.y != size) && (Mapa[destra.x][destra.y] == -1)) {
            //currentDest =destra;
            Mapa[destra.x][destra.y] = 5;
            addVertex(destra);
        }

        if ((p.getNeighborhood().westIsFree()) && (posizioneattuale.y != 0) && (Mapa[sinistra.x][sinistra.y] == -1)) {  //currentDest = sinistra;
            Mapa[sinistra.x][sinistra.y] = 5;
            addVertex(sinistra);
        }

        if ((p.getNeighborhood().northIsFree()) && (posizioneattuale.x != size) && (Mapa[sotto.x][sotto.y] == -1)) { //currentDest = sotto;
            Mapa[sotto.x][sotto.y] = 5;
            addVertex(sotto);
        }
    }

    private void NextDest(VAPercept percept) {
        UpDate(percept);
        setPolvere();
        for (int i = 0; i < nonVisitato.size(); i++) {
            Collections.sort(nonVisitato);
        }
        if (nonVisitato.isEmpty()) {
            currentDest = originalPosition;
        } else {
            currentDest = nonVisitato.get(0).getPosition();
        }



    }

    private void setOriginalPosition(Point ori) {
        if (this.originalPosition == null) {
            this.originalPosition = new Point(ori);
        }
    }

    private VAAction nextAction(VAPercept percept) {

        VAAction action = new VAAction(VAAction.VAActionType.NOOP);
        Point posizioneattuale = percept.getVacuumAgentPosition();

        Mapa[posizioneattuale.x][posizioneattuale.y] = 0;
        addVertex(posizioneattuale);
        if (percept.getCurrentTileStatus() == VATileStatus.DIRTY) {
            action.setActionType(VAAction.VAActionType.SUCK);
            return action;
        }

        Point tmpDST = null;

        if (!path.isEmpty()) {
            tmpDST = path.remove(0);
        } else if ((path.isEmpty() && (posizioneattuale.x + 1 == currentDest.x) || (posizioneattuale.x - 1 == currentDest.x) || (posizioneattuale.y + 1 == currentDest.y) || (posizioneattuale.y - 1 == currentDest.y))) {
            tmpDST = currentDest;
        }

        if (tmpDST.x > currentPosition.x) {
//            action.setActionType(VAAction.VAActionType.MOVENORTH);
            action.setActionType(VAAction.VAActionType.MOVESOUTH);
        } else if (tmpDST.x < currentPosition.x) {
//            action.setActionType(VAAction.VAActionType.MOVESOUTH);
            action.setActionType(VAAction.VAActionType.MOVENORTH);
        } else if (tmpDST.y > currentPosition.y) {
            action.setActionType(VAAction.VAActionType.MOVEEAST);
        } else if (tmpDST.y < currentPosition.y) {
            action.setActionType(VAAction.VAActionType.MOVEWEST);
        }

        return action;
    }

    private void setMinPath() {
        List<DefaultWeightedEdge> edgeList = DijkstraShortestPath.findPathBetween(Grafo, currentPosition, currentDest);
        int count = 0;
        for (Iterator<DefaultWeightedEdge> it = edgeList.iterator(); it.hasNext();) {
            DefaultWeightedEdge defaultWeightedEdge = it.next();
            path.add(Grafo.getEdgeSource(defaultWeightedEdge));
        }
        if (path.size() > 0) {
            path.add(Grafo.getEdgeTarget(edgeList.get(edgeList.size() - 1)));
            path.remove(0);
        }
        System.out.println("CURRENT POS = " + currentPosition);
        System.out.println("CURRENT DES = " + currentDest);
        System.out.println("STAMPO IL PATH\n");
        System.out.println(path);
        System.out.println("\n");
    }

    @Override
    public Action execute(Percept percept) {


        VAPercept percezione = (VAPercept) percept;
        int size = percezione.getFloor().getSize();
        if (Mapa == null && test == false) {
            test = true;
            Mapa = new int[size][size];
            setmapa(size, (VAPercept) percept);
        }
        //setPolvere();
        currentPosition = ((VAPercept) percept).getVacuumAgentPosition();
        UpDate((VAPercept) percept);
        if (originalPosition == null) {
            setOriginalPosition(currentPosition);
        }
        if (path.isEmpty() && currentPosition.equals(currentDest)) {
            reached = true;
        }
        UpDate((VAPercept) percept);
        if (reached) {
            UpDate((VAPercept) percept);
            NextDest((VAPercept) percept);
            UpDate((VAPercept) percept);
            
            System.out.println("CERCO IL PATH MINIMO");

            setMinPath();
            if (path.contains(currentDest)) {
                path.remove(currentDest);
            }
            reached = false;
        }

        VAAction move = nextAction((VAPercept) percept);
        return move;

    }
}
